import 'package:easy_refresh/easy_refresh.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:im_app/im/service/ImMessageService.dart';
import 'package:im_app/im/socket/dto/business/SendData.dart';
import 'package:im_app/im/util/IsolatePoolUtil.dart';

import '../../dao/ImGroupDao.dart';
import '../../dao/ImGroupUserDao.dart';
import '../../dao/ImSessionDao.dart';
import '../../pojo/Enums.dart';
import '../../pojo/ImPojo.dart';
import '../../socket/service/RpcSendDataService.dart';
import '../../util/ImConstant.dart';
import '../../util/ImSingleton.dart';

final class SessionListCubit extends Cubit<SessionListState> {
  SessionListCubit() : super(const SessionListState.loading());

  Future<IndicatorResult> loadData(String userId) async {
    print("SessionListCubit load!!");
    List<ImSession> sessions = await _loadSessions(userId);
    // List<ImSession> sessions = await IsolatePoolUtil.getInstance().compute(this._loadSessions, userId);
    var hotMessageTotal = calacHotMessage(sessions);
    emit(SessionListState.success(sessions, hotMessageTotal));
    return IndicatorResult.success;
  }

  void addSession(ImSession session) {
    if (has(session)) return;
    List<ImSession> sessions = List.of(state.sessions);
    sessions.insert(0, session);

    var hotMessageTotal = calacHotMessage(sessions);
    emit(SessionListState.success(sessions, hotMessageTotal));
  }

  void changeSession(ImSession session) {
    var sessionDao = ImSessionDao();
    print("changeSession, ${session.id}");
    List<ImSession> sessions = List.of(state.sessions);
    var index = indexOf(session);
    if (index >= 0) {
      sessions.replaceRange(index, index+1, [session]);
    }
    sessionDao.sort(sessions);
    var hotMessageTotal = calacHotMessage(sessions);
    emit(SessionListState.success(sessions, hotMessageTotal));
  }

  Future<void> hideSession(ImSession session) async {
    final sessionDao = ImSessionDao();
    session.updateTime = DateTime.now().add(const Duration(days: -15));
    await sessionDao.batchUpdates(session.currentUserId!, [session]);

    List<ImSession> sessions = List.of(state.sessions);
    var index = indexOf(session);
    if (index >= 0) {
      sessions.removeAt(index);
    }

    var hotMessageTotal = sessions.map((session)=>session.hotMessageTotal??0).reduce((value, v) => value + v);
    emit(SessionListState.success(sessions, hotMessageTotal));
  }

  Future<void> deleteSession(ImSession session) async {
    final messageService = ImMessageService();
    final sessionDao = ImSessionDao();

    await messageService.cleanMessage(session.currentUserId!, session.id!);

    session.status = ImSessionStatus.disable;
    session.updateTime = DateTime.now();
    await sessionDao.batchUpdates(session.currentUserId!, [session]);

    List<ImSession> sessions = List.of(state.sessions);
    var index = indexOf(session);
    if (index >= 0) {
      sessions.removeAt(index);
    }
    var hotMessageTotal = calacHotMessage(sessions);
    emit(SessionListState.success(sessions, hotMessageTotal));
  }

  Future<void> sessionTop(ImSession session, bool isTop) async {
    var sessionDao = ImSessionDao();
    var groupUserDao = ImGroupUserDao();
    var groupDao = ImGroupDao();

    var rpcMessageService = RpcMessageService();
    var userId = ImSingleton.getInstance().session.userId!;

    if (session.groupCode != null) { // 群会话
      ImGroup? imGroup = await groupDao.getByCodeAndUserId(session.groupCode!, userId);
      ImGroupUser groupUser = imGroup!.currentGroupUser!;
      groupUser.isTop = isTop;
      groupUser.updateTime = DateTime.now();
      await groupUserDao.batchUpdates([groupUser].toSet());
    } else {
      if (session.fromUserId!.compareTo(session.toUserId!) >= 0) {
        session.isTop = isTop;
      } else {
        session.isTop2 = isTop;
      }
      session.updateTime = DateTime.now();
    }

    var key = session.groupCode != null ? session.groupCode : session.toUserId;
    var dataChangeReq = DataChangeReq(type: DataType.session,
        subType: DataSubType.s_ed_top,
        value: isTop.toString(),
        key: key!);
    await rpcMessageService.sendDataChange(dataChangeReq);

    await sessionDao.batchUpdates(userId, [session]);
    changeSession(session);
  }

  Future<void> sessionDisturb(ImSession session, bool isDisturb) async {
    var sessionDao = ImSessionDao();
    var groupUserDao = ImGroupUserDao();
    var groupDao = ImGroupDao();
    var rpcMessageService = RpcMessageService();
    var userId = ImSingleton.getInstance().session.userId!;
    if (session.groupCode != null) { // 群会话
      ImGroup? imGroup = await groupDao.getByCodeAndUserId(session.groupCode!, userId);
      ImGroupUser groupUser = imGroup!.currentGroupUser!;
      groupUser.isDisturb = isDisturb;
      groupUser.updateTime = DateTime.now();
      await groupUserDao.batchUpdates([groupUser].toSet());
    } else {
      if (session.fromUserId!.compareTo(session.toUserId!) >= 0) {
        session.isDisturb = isDisturb;
      } else {
        session.isDisturb2 = isDisturb;
      }
      session.updateTime = DateTime.now();
    }

    var key = session.groupCode != null ? session.groupCode : session.toUserId;
    var dataChangeReq = DataChangeReq(type: DataType.session,
        subType: DataSubType.s_ed_disturb,
        value: isDisturb.toString(),
        key: key!);
    await rpcMessageService.sendDataChange(dataChangeReq);

    await sessionDao.batchUpdates(userId, [session]);
    changeSession(session);
  }

  Future<void> reload(String userId) async {
    print("SessionListCubit reload!!");
    await loadData(userId);
  }

  void increaseHotMessageTotal(int total) {
    emit(SessionListState.success(state.sessions, state.hotMessageTotal + total));
  }

  bool has(ImSession session) {
    return indexOf(session) >= 0;
  }

  int indexOf(ImSession imSession) {
    int index = 0;
    for (var session in state.sessions) {
      if (session.id == imSession.id) {
        return index;
      }
      index++;
    }
    return -1;
  }

  int calacHotMessage(List<ImSession> sessions) {
    if (sessions.isNotEmpty) {
      var hotMessageTotal = sessions.map((session)=>session.hotMessageTotal??0).reduce((value, v) => value + v);
      return hotMessageTotal;
    }
    return 0;
  }

  Future<List<ImSession>> _loadSessions(String userId) async {
    var sessionDao = ImSessionDao();
    var startDate = DateTime.now().add(const Duration(days: -ImConstant.IM_SESSION_SHOW_DAYS));
    List<ImSession> sessions = await sessionDao.getByUserIdAndTimeAndSize(userId, startDate, 0, ImConstant.SESSION_MAX_SIZE);
    if (sessions.isNotEmpty) {
      for (var session in sessions) {
        await sessionDao.loadGroupInfo(session);
      }
      sessionDao.sort(sessions);
    }
    return sessions;
  }
}

final class SessionListState extends Equatable {

  final LoadingState state;
  final List<ImSession> sessions;
  final int hotMessageTotal;

  const SessionListState._({
    this.state = LoadingState.loading,
    this.sessions = const <ImSession>[],
    this.hotMessageTotal = 0,
  });

  const SessionListState.loading() : this._();

  const SessionListState.success(List<ImSession> sessions, int hotMessageTotal)
      : this._(state: LoadingState.success, sessions: sessions, hotMessageTotal: hotMessageTotal);

  const SessionListState.failure() : this._(state: LoadingState.failure);

  @override
  List<Object?> get props => [state, sessions, hotMessageTotal];

}